/*
 * @Description:
 * @FilePath: /STM32F103/USER/bsp/uart.c
 * @Author: Wei Zhou
 * @Github: https://github.com/zromyk
 * @Date: 2021-04-28 15:25:19
 * @LastEditors: Wei Zhou
 * @LastEditTime: 2021-05-12 22:21:29
 * @Copyright: Copyright ? 2017 muyiro. All rights reserved.
 */

#include "uart.h"
#include "nvic.h"
#include "rcc.h"

/* Select which USART is the debug usart. */
static USART_TypeDef* USARTx_Debug = DEBUG_USART;

#ifdef USE_FULL_ASSERT

/**
 * @brief  Reports the name of the source file and the source line number
 *         where the assert_param error has occurred.
 * @param  file: pointer to the source file name
 * @param  line: assert_param error line source number
 * @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
    /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line)   */

    /* Infinite loop    */
    while (1)
    {
        printf("Wrong parameters value: file %s on line %d\r\n", file, line)
    }
}

#endif

/**
* @brief  Retargets the C library printf function to the USART.
* @param  None
* @retval None
*/
#pragma import(__use_no_semihosting)

/* 标准库需要的支持函数 */
struct __FILE
{
    int handle;
};

FILE __stdout;

/* 定义 _sys_exit() 以避免使用半主机模式     */
void _sys_exit(int x)
{
    x = x;
}

/* 重定义 fputc 函数  */
int fputc(int ch, FILE *f)
{
    while((USARTx_Debug->SR & 0X40) == 0);
    USARTx_Debug->DR = (uint8_t)ch;
    return ch;
}

uint8_t TxBuffer_USART1[TxBuffer_Size_USART1];
uint8_t RxBuffer_USART1[RxBuffer_Size_USART1];

uint8_t TxBuffer_USART2[TxBuffer_Size_USART2];
uint8_t RxBuffer_USART2[RxBuffer_Size_USART2];

uint8_t TxBuffer_USART3[TxBuffer_Size_USART3];
uint8_t RxBuffer_USART3[RxBuffer_Size_USART3];

USART_Dev usart1 =
{
    .USARTx                 = USART1,
    .RCC_Periph_USART       = RCC_APB2Periph_USART1,
    /*
                    0
        USART1_TX   PA9
        USART1_RX   PA10
    */
    .pin_tx                 = PA9,
    .pin_rx                 = PA10,

    .tx_buf                 = TxBuffer_USART1,
    .rx_buf                 = RxBuffer_USART1,
    .tx_buf_size            = TxBuffer_Size_USART1,
    .rx_buf_size            = RxBuffer_Size_USART1,
    .rx_cnt                 = 0,

    .USARtx_IRQn            = USART1_IRQn,

    /*
        USART1_TX   DMA1
                    channel 4

        USART1_RX   DMA1
                    channel 5
    */
    .dma.DMAx               = DMA1,
    .dma.RCC_Periph_DMA     = RCC_AHBPeriph_DMA1,

    .dma.tx_channel         = DMA1_Channel4,
    .dma.tx_IRQn            = DMA1_Channel4_IRQn,
    .dma.tx_FLAG_TC         = DMA1_FLAG_TC4,

    .dma.rx_channel         = DMA1_Channel5,
    .dma.rx_IRQn            = DMA1_Channel5_IRQn,    /* or Stream 5 */
    .dma.rx_FLAG_TC         = DMA1_FLAG_TC5,
};

USART_Dev usart2 =
{
    .USARTx                 = USART2,
    .RCC_Periph_USART       = RCC_APB1Periph_USART2,
    /*
                    0
        USART2_TX   PA2
        USART2_RX   PA3
    */
    .pin_tx                 = PA2,
    .pin_rx                 = PA3,

    .tx_buf                 = TxBuffer_USART2,
    .rx_buf                 = RxBuffer_USART2,
    .tx_buf_size            = TxBuffer_Size_USART2,
    .rx_buf_size            = RxBuffer_Size_USART2,
    .rx_cnt                 = 0,

    .USARtx_IRQn            = USART2_IRQn,

    /*
        USART2_TX   DMA1
                    channel 5

        USART2_RX   DMA1
                    channel 6
    */
    .dma.DMAx               = DMA1,
    .dma.RCC_Periph_DMA     = RCC_AHBPeriph_DMA1,

    .dma.tx_channel         = DMA1_Channel5,
    .dma.tx_IRQn            = DMA1_Channel5_IRQn,
    .dma.tx_FLAG_TC         = DMA1_FLAG_TC5,

    .dma.rx_channel         = DMA1_Channel6,
    .dma.rx_IRQn            = DMA1_Channel6_IRQn,
    .dma.rx_FLAG_TC         = DMA1_FLAG_TC6,
};

USART_Dev usart3 =
{
    .USARTx                 = USART3,
    .RCC_Periph_USART       = RCC_APB1Periph_USART3,
    /*
                    0       1 (AF map)
        USART3_TX   PB10    --PC10--
        USART3_RX   PB11    --PC11--
    */
    .pin_tx                 = PB10,
    .pin_rx                 = PB11,

    .tx_buf                 = TxBuffer_USART3,
    .rx_buf                 = RxBuffer_USART3,
    .tx_buf_size            = TxBuffer_Size_USART3,
    .rx_buf_size            = RxBuffer_Size_USART3,
    .rx_cnt                 = 0,

    .USARtx_IRQn            = USART3_IRQn,

    /*
        USART3_TX   DMA1
                    channel 2

        USART3_RX   DMA1
                    channel 3
    */
    .dma.DMAx               = DMA1,
    .dma.RCC_Periph_DMA     = RCC_AHBPeriph_DMA1,

    .dma.tx_channel         = DMA1_Channel2,
    .dma.tx_IRQn            = DMA1_Channel2_IRQn,
    .dma.tx_FLAG_TC         = DMA1_FLAG_TC2,

    .dma.rx_channel         = DMA1_Channel3,
    .dma.rx_IRQn            = DMA1_Channel3_IRQn,
    .dma.rx_FLAG_TC         = DMA1_FLAG_TC3,
};

static void gpio_configuration(USART_Dev *usart)
{
    gpio_af_init(usart->pin_tx);
    gpio_in_init(usart->pin_rx, GPIO_PuPd_NOPULL);
}

static void usart_configuration(USART_Dev *usart, uint32_t baudRate)
{
    USART_InitTypeDef USART_InitStruct;

    rcc_periph_clock_enable(usart->USARTx, usart->RCC_Periph_USART);

    /* Enable the USART2 Pins Software Remapping */
    /* GPIO_PinRemapConfig(usart->GPIO_Remap_USART, ENABLE); */

    /* USART configuration -------------------------------------------*/
      /* USART configured as follow:
            - BaudRate = (baudRate) baud
            - Word Length = 8 Bits
            - One Stop Bit
            - No parity
            - Hardware flow control disabled (RTS and CTS signals)
            - Receive and transmit enabled
      */
    USART_InitStruct.USART_BaudRate      = baudRate;
    USART_InitStruct.USART_WordLength    = USART_WordLength_8b;
    USART_InitStruct.USART_StopBits      = USART_StopBits_1;
    USART_InitStruct.USART_Parity        = USART_Parity_No;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode          = USART_Mode_Rx | USART_Mode_Tx;

    /* Configure the USARTx */
    USART_Init(usart->USARTx, &USART_InitStruct);
}

void usart_init(USART_Dev *usart, uint32_t baudRate, uint8_t priority, uint8_t subPriority)
{
    /* assert_param(IS_USART(usart->USARTx)); */

    gpio_configuration(usart);
    usart_configuration(usart, baudRate);

    /* Enable USART1 Receive interrupts */
    nvic_configuration(usart->USARtx_IRQn, priority, subPriority);
    USART_ITConfig(usart->USARTx, USART_IT_RXNE, ENABLE);

    /* Enable the USARTx */
    USART_Cmd(usart->USARTx, ENABLE);
}

void usart_user_define_init_init(USART_Dev *usart, USART_InitTypeDef *init_struct, uint8_t priority, uint8_t subPriority)
{
    gpio_configuration(usart);

    /* usart_configuration(usart, baudRate); */
    rcc_periph_clock_enable(usart->USARTx, usart->RCC_Periph_USART);
    USART_Init(usart->USARTx, init_struct); /* Configure the USARTx */

    /* Enable USART1 Receive interrupts */
    nvic_configuration(usart->USARtx_IRQn, priority, subPriority);
    USART_ITConfig(usart->USARTx, USART_IT_RXNE, ENABLE);

    /* Enable the USARTx */
    USART_Cmd(usart->USARTx, ENABLE);
}

void usart_dma_rx_init(USART_Dev *usart)
{
    dma_rx_init(&usart->dma, (uint32_t)&usart->USARTx->DR,
                usart->rx_buf, usart->rx_buf_size);

    USART_ITConfig(usart->USARTx, USART_IT_RXNE, DISABLE);
    USART_ITConfig(usart->USARTx, USART_IT_IDLE, ENABLE);

    DMA_ITConfig(usart->dma.rx_channel, DMA_IT_TC, ENABLE);
    DMA_ITConfig(usart->dma.rx_channel, DMA_IT_TE, ENABLE);

    USART_DMACmd(usart->USARTx, USART_DMAReq_Rx, ENABLE);

    DMA_Cmd(usart->dma.rx_channel, ENABLE);
}

void usart_dma_tx_init(USART_Dev *usart, uint8_t priority, uint8_t subPriority)
{
    nvic_configuration(usart->dma.tx_IRQn, priority, subPriority);

    dma_tx_init(&usart->dma, (uint32_t)&usart->USARTx->DR,
                usart->tx_buf, usart->tx_buf_size);

    DMA_ITConfig(usart->dma.tx_channel, DMA_IT_TC, ENABLE);
    DMA_ITConfig(usart->dma.tx_channel, DMA_IT_TE, ENABLE);

    USART_DMACmd(usart->USARTx, USART_DMAReq_Tx, ENABLE);
}

void usart_dma_tx_size_enable(USART_Dev *usart, uint32_t size)
{
    while (USART_GetFlagStatus(usart->USARTx, USART_FLAG_TC) == RESET)
        ;
    while (USART_GetFlagStatus(usart->USARTx, USART_FLAG_TXE) == RESET)
        ;

    USART_ClearFlag(usart->USARTx, USART_FLAG_TC);

    DMA_Cmd(usart->dma.tx_channel, DISABLE);
    DMA_SetCurrDataCounter(usart->dma.tx_channel, size);
    DMA_Cmd(usart->dma.tx_channel, ENABLE);
}

void usart_dma_tx_enable(USART_Dev *usart)
{
    usart_dma_tx_size_enable(usart, usart->tx_buf_size);
}

void usart_write_byte(USART_Dev *usart, const uint8_t byte)
{
    USART_SendData(usart->USARTx, (uint8_t)byte);
    while (USART_GetFlagStatus(usart->USARTx, USART_FLAG_TXE) == RESET)
        ;
}

void usart_write_buffer(USART_Dev *usart, const uint8_t *buf, uint32_t length)
{
    uint32_t i;

    for (i = 0; i < length; ++i)
    {
        USART_SendData(usart->USARTx, (uint8_t)buf[i]);
        while (USART_GetFlagStatus(usart->USARTx, USART_FLAG_TXE) == RESET)
            ;
    }
}

void usart_write_string(USART_Dev *usart, const uint8_t *str)
{
    while ((*str) != '\0')
    {
        USART_SendData(usart->USARTx, (uint8_t)*str);
        while (USART_GetFlagStatus(usart->USARTx, USART_FLAG_TXE) == RESET)
            ;
        str++;
    }
}

void usart_reply(const uint8_t *buf, uint32_t length)
{
    uint32_t i;

    for (i = 0; i < length; ++i)
    {
        USART_SendData(USARTx_Debug, *buf);
        while (USART_GetFlagStatus(USARTx_Debug, USART_FLAG_TXE) == RESET)
            ;
        buf++;
    }
    USART_SendData(USARTx_Debug, '\n');
    while (USART_GetFlagStatus(USARTx_Debug, USART_FLAG_TXE) == RESET)
        ;
}
